package hhh.app.data.repository;

import android.content.Context;

import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Singleton;

import hhh.app.common.NetUtils;
import hhh.app.common.error.NetworkConnectionException;
import hhh.app.data.bean.Cart;
import hhh.app.data.bean.Category;
import hhh.app.data.bean.Product;
import hhh.app.data.bean.ProductDiscount;
import hhh.app.data.bean.ProductImage;
import hhh.app.data.bean.ProductOption;
import hhh.app.data.bean.Review;
import hhh.app.data.cache.CacheLoader;
import hhh.app.data.cache.NetworkCache;
import hhh.app.data.net.ApiImp;
import hhh.app.common.Code;
import hhh.app.domain.repository.ProductRepository;
import rx.Observable;
import rx.functions.Action1;

/**
 * Created by hhh on 2016/10/28.
 */
@Singleton
public class ProductDataRepository implements ProductRepository {
    private CacheLoader cache;
    private ApiImp apiImp;
    private Context context;

    @Inject
    public ProductDataRepository(Context context){
        this.context=context;
        cache =new CacheLoader(context);
        apiImp=new ApiImp(context,Code.serverUrl);
    }

    private String buildKey(Map<String,String> map){
        StringBuffer sb=new StringBuffer();
        sb.append(map.get("page"))
                .append("_")
                .append(map.get("num"))
                .append("_")
                .append(map.get("category"))
                .append("_")
                .append(map.get("word"))
                .append("_")
                .append(map.get("sort"));
        return sb.toString();
    }

    private Observable<Product> getProductCacheObservable(final String key){
        NetworkCache<Product> networkCache=new NetworkCache<Product>() {
            @Override
            public Observable<Product> get(String key,Type t) {
                return getProductLoadObservable(key);
            }
        };
        return cache.asDatasouce(key,Product.class,networkCache);
    }

    private Observable<Product> getProductLoadObservable(final String key){
        return apiImp.getProductDetail(Integer.valueOf(key))
                .doOnNext(new Action1<Product>() {
                    @Override
                    public void call(Product product) {
                        cache.upNewData(key,product);
                    }
                });
    }

    private Observable<List<Product>> getProductCacheListLoadObservable(final Map<String,String> map){
        NetworkCache<List<Product>> networkCache=new NetworkCache<List<Product>>(){
            @Override
            public Observable<List<Product>> get(String key,Type t) {
                return getProductListLoadObservable(map);
            }
        };
        Type t=new TypeToken<ArrayList<Product>>(){}.getType();

        return cache.<List<Product>>asDatasouce(buildKey(map), t,networkCache);
    }

    private Observable<List<Product>> getProductListLoadObservable(final Map<String,String> map){
        return apiImp.getProductList(map)
                .doOnNext(new Action1<List<Product>>() {
                    @Override
                    public void call(List<Product> products) {
                        if("1".equals(map.get("page"))){
                            cache.upNewData(buildKey(map),products);
                        }
                    }
                });
    }

    @Override
    public Observable<Product> product(int pid) {
        if(NetUtils.isConnected(context)){
            return getProductLoadObservable(String.valueOf(pid));
        }else{
            return getProductCacheObservable(String.valueOf(pid));
        }
    }

    @Override
    public Observable<List<Product>> products(Map<String,String> map) {
        if(NetUtils.isConnected(context)){
            return getProductListLoadObservable(map);
        }else if("1".equals(map.get("page"))){
            return getProductCacheListLoadObservable(map);
        }else{
            return Observable.error(new NetworkConnectionException("网络断开"));
        }
    }

    @Override
    public Observable<List<Category>> categories() {
        if(NetUtils.isConnected(context)){
            return getCategoriesLoadObservable();
        }else {
            return getCategoriesCacheObservable();
        }
    }

    @Override
    public Observable<List<ProductDiscount>> productDiscount(int pid) {
        return apiImp.getProductDiscount(pid);
    }

    @Override
    public Observable<List<ProductImage>> productImage(int pid) {
        return apiImp.getProductImage(pid);
    }

    @Override
    public Observable<List<ProductOption>> productOption(int pid) {
        return apiImp.getProductOption(pid);
    }

    @Override
    public Observable<List<Review>> productReviews(int pid) {
        return apiImp.getProductReviews(pid);
    }

    @Override
    public Observable<List<Cart>> carts(int uid) {
        return apiImp.getCarts(uid);
    }

    private Observable<List<Category>> getCategoriesCacheObservable() {
        NetworkCache<List<Category>> networkCache=new NetworkCache<List<Category>>() {
            @Override
            public Observable<List<Category>> get(String key, Type type) {
                return getCategoriesLoadObservable();
            }
        };
        Type t = new TypeToken<List<Category>>(){}.getType();
        return cache.asDatasouce(Code.CATEGORY_KEY,t,networkCache);
    }

    private Observable<List<Category>> getCategoriesLoadObservable() {
        return apiImp.getCategories()
                .doOnNext(new Action1<List<Category>>() {
                    @Override
                    public void call(List<Category> categories) {
                        if(categories!=null){
                            cache.upNewData(Code.CATEGORY_KEY,categories);
                        }
                    }
                });
    }
}
